#include<stdio.h>
#include<Algorithm/qem.h>
#include<Mesh_IO/Mesh_IO.h>
#include<Mesh/libcell_iterator.h>

#include<tool/libcell_octree_iterator.h>

#define quote lib_cell_quote
static int is_in_one_texture_param_area(Mesh* m,Edge_Energy*ee)
{
    template_f*f=m->get_facep(m,ee->fid);
    template_v* v1=f->vertices[0],*v2=f->vertices[1];
    template_c*c=NULL;
    c=(template_c*)(v1->cells->value); 
    OBJ_CELL_PROP*ocp=(OBJ_CELL_PROP*)(c->prop);
    int j=0;
    for(int i=0;i<c->vertices_size;i++)
    {
        if(c->vertices[i]==v1)
        {
            j=i; break;
        }
    }
    for(auto vcit=m->vc_begin(m,*v1);vcit!=m->vc_end(m,*v1);vcit++)
    {
        int k=0;
        for(int i=0;i<quote(vcit)->vertices_size;i++)
        {
            if(quote(vcit)->vertices[i]==v1)
            {
                k=i;break;
            }
        } 
        OBJ_CELL_PROP* ocp1=(OBJ_CELL_PROP*)(quote(vcit)->prop); 
        if(fabs(ocp1->vt[k][0]-ocp->vt[j][0])>=3e-4||fabs(ocp1->vt[k][1]-ocp->vt[j][1])>=3e-4||strcmp(ocp1->mtlname,ocp->mtlname)!=0)
        {
            return 0;
        }      
    }
    c=(template_c*)(v2->cells->value);
    ocp=(OBJ_CELL_PROP*)(c->prop); 
    for(int i=0;i<c->vertices_size;i++)
    {
        if(c->vertices[i]==v2)
        {
            j=i; break;
        }
    }
    for(auto vcit=m->vc_begin(m,*v2);vcit!=m->vc_end(m,*v2);vcit++)
    {
        int k=0;
        for(int i=0;i<quote(vcit)->vertices_size;i++)
        {
            if(quote(vcit)->vertices[i]==v2)
            {
                k=i;break;
            }
        } 
        OBJ_CELL_PROP* ocp1=(OBJ_CELL_PROP*)(quote(vcit)->prop);  

        if(fabs(ocp1->vt[k][0]-ocp->vt[j][0])>=3e-4||fabs(ocp1->vt[k][1]-ocp->vt[j][1])>=3e-4||strcmp(ocp1->mtlname,ocp->mtlname)!=0)
        {
            return 0;
        }     
    }
    return 1; 
}


template_v* my_qem_collapse_edge(Mesh*m,Edge_Energy*ee,Int_RB_Tree*tree_v,CHeap*ch)
{
    if(ee==NULL||ee->p==NULL)
    {
        return NULL;
    } 

    template_f* f=m->get_facep(m,ee->fid);
    if(f==NULL)
    {
        return NULL;
    }  
    template_v* v1=f->vertices[0],*v2=f->vertices[1];
    if(is_in_one_texture_param_area(m,ee)==0)
    {
        return NULL;
    }
    Int_RB_Tree* cells=(Int_RB_Tree*)malloc(sizeof(Int_RB_Tree));
    int_rb_tree_init(cells); 

    for(auto vcit=m->vc_begin(m,*v1);vcit!=m->vc_end(m,*v1);vcit++)
    {
        cells->insert(cells,quote(vcit)->id,quote(vcit)); 
    }
    for(auto vcit=m->vc_begin(m,*v2);vcit!=m->vc_end(m,*v2);vcit++)
    {
        cells->insert(cells,quote(vcit)->id,quote(vcit));  
    }

    Node* boundary_hfaces=NULL;
    Node* node_ocps=NULL;
    double* temp_vt=NULL; 
    for(auto it=cells->begin(cells);it.it!=NULL;it++)
    {
        template_c* c=(template_c*)(it.second); 
        OBJ_CELL_PROP *ocp=(OBJ_CELL_PROP*)(c->prop);
        for(auto chfit=m->chf_begin(m,*c);chfit!=m->chf_end(m,*c);chfit++)
        {
            template_hf* hf=quote(chfit);
            if(hf->vertices[0]!=v1&&hf->vertices[0]!=v2&&hf->vertices[1]!=v1&&hf->vertices[1]!=v2)
            {
                Node *hfs=NULL;
                hfs=node_overlying(hfs,hf->vertices[1]);
                hfs=node_overlying(hfs,hf->vertices[0]);
                boundary_hfaces=node_overlying(boundary_hfaces,hfs); 

                if(ocp!=NULL)
                {
                    OBJ_CELL_PROP*ocp1=(OBJ_CELL_PROP*)malloc(sizeof(OBJ_CELL_PROP));
                    obj_cell_prop_init(ocp1);
                    if(ocp->mtlname!=NULL&&strlen(ocp->mtlname)!=0)
                    {
                        ocp1->mtlname=(char*)malloc(sizeof(char)*(strlen(ocp->mtlname)+1));
                        strcpy(ocp1->mtlname,ocp->mtlname);
                    }  
                    if(ocp->group_info!=NULL&&strlen(ocp->group_info)!=0)
                    {
                        ocp1->group_info=(char*)malloc(sizeof(char)*(strlen(ocp->group_info)+1));
                        strcpy(ocp1->group_info,ocp->group_info);
                    }

                    boundary_hfaces->traits=ocp1;
                    template_v*vs[3]={hf->vertices[0],hf->vertices[1],NULL};
                    int mark[3]={0,0,0};
                    for(int i=0;i<3;i++)
                    {
                        vs[0]==c->vertices[i]?mark[i]=0:(vs[1]==c->vertices[i]?mark[i]=1:mark[i]=2);
                    } 
                    ocp1->vt=(double**)malloc(sizeof(double*)*3);
                    ocp1->n=3;
                    for(int i=0;i<3;i++)
                    {
                        int k=mark[i]; 
                        ocp1->vt[k]=(double*)malloc(sizeof(double)*2);
                        if(k==2)
                        {
                            if(temp_vt==NULL)
                            {
                                double*coord= get_triangle_coordinate(ee->p,c->vertices[0]->point,c->vertices[1]->point,c->vertices[2]->point); 
                                temp_vt=(double*)malloc(sizeof(double)*2);
                                if(coord==NULL)
                                {
                                    temp_vt[0]=ocp->vt[i][0];temp_vt[1]=ocp->vt[i][1];
                                }
                                else
                                {
                                    for(int j=0;j<2;j++)
                                    {
                                        temp_vt[j]=coord[0]*ocp->vt[0][j]+coord[1]*ocp->vt[1][j]+coord[2]*ocp->vt[2][j];
                                    }
                                }
                                SAFE_FREE(coord);
                            }
                            ocp1->vt[k][0]=temp_vt[0];ocp1->vt[k][1]=temp_vt[1];
                        }
                        else{
                            ocp1->vt[k][0]=ocp->vt[i][0];ocp1->vt[k][1]=ocp->vt[i][1];    
                        }
                    }
                }  
            } 
        }   
        node_ocps=node_overlying(node_ocps,c->prop);
    }
    free(temp_vt);

    template_v* n_v=qem_collapse_edge(m,ee,tree_v,ch);
    for(Node*nit=boundary_hfaces;nit!=NULL;nit=(Node*)(nit->Next))
    {
        template_c* c=NULL;
        if(n_v!=NULL)
        {
            template_v*vs[3]={NULL,NULL,n_v};
            int i=0;
            for(Node*nit1=(Node*)(nit->value);nit1!=NULL;nit1=(Node*)(nit1->Next))
            {
                vs[i]=(template_v*)(nit1->value);
                i++;
            }
           
            c=m->get_cellv(m,vs,3); 
        } 
        if(c==NULL)
        {

            obj_cell_prop_free((OBJ_CELL_PROP*)(nit->traits));
        }
        else
        {
            c->prop=nit->traits;
        } 
        nit->traits=NULL;
        free_node((Node*)(nit->value));
    }
    free_node(boundary_hfaces); 
    int_rb_tree_free(cells);
    if(n_v!=NULL)
    {
        for(Node*nit=node_ocps;nit!=NULL;nit=(Node*)(nit->Next))
        {
            obj_cell_prop_free((OBJ_CELL_PROP*)(nit->value));
        }
        free_node(node_ocps);
    }
    return n_v;

} 
void zydl_simplified_mesh(char* in_path,char* out_path,double fac)
{
    Mesh mesh;
    Mesh_init(&mesh);
    printf("begin read obj\n");
    _ReadObj_(&mesh,in_path);
    printf("end read obj\n");
    Int_RB_Tree *tree=qem_init_qi(&mesh);
    printf("begin compute qem\n"); 
    CHeap*ch=compute_qem_all_edge_position_and_energy(&mesh,tree);
    printf("end compuate qem\n"); 
    if(*((Edge_Energy**)(((char*)ch->values)+ch->size*ch->width))!=NULL)
    {
        printf("zheli cuowu\n");
    }

    int mylen=mesh.num_c(&mesh)*((double)(1-fac)*0.5); 
 
    printf("size:%d\n",ch->size);
    for(int i=0;i<mylen&&ch->size>0;i++)
    {
        void* value=pop_cheap(ch);
        Edge_Energy*ee=*((Edge_Energy**)(value)); 

        //printf("%d\n",ch->size);
        if(my_qem_collapse_edge(&mesh,ee,tree,ch)==NULL)
        {
            i--;
        }

        edge_energy_free(ee);
        free(value);

    }
    printf("num v:%d num_c :%d\n",mesh.num_v(&mesh),mesh.num_c(&mesh));
    OBJ_MESH_PROP* omp=(OBJ_MESH_PROP*)(mesh.prop);
    Node*node=NULL;
    for(auto cit=mesh.c_begin(&mesh);cit!=mesh.c_end(&mesh);cit++)
    {
        node=node_overlying(node,quote(cit));
    }
    export_obj_from_node(node,out_path,omp->mtlibname); 
    free_node(node);

    for(auto it=tree->begin(tree);it.it!=NULL;it++)
    {
        lb_matrix_free((LB_Matrix*)(it.second));
    }
    int_rb_tree_free(tree);
    for(int i=0;i<ch->size;i++)
    {
        Edge_Energy*ee=*((Edge_Energy**)(((char*)(ch->values))+i*ch->width));
        edge_energy_free(ee); 
    } 
    free(ch->values);
    free(ch);
    for(auto cit=mesh.c_begin(&mesh);cit!=mesh.c_end(&mesh);cit++)
    { 

        obj_cell_prop_free((OBJ_CELL_PROP*)(quote(cit)->prop));quote(cit)->prop=NULL;
    }
    obj_mesh_prop_free((OBJ_MESH_PROP*)(mesh.prop));mesh.prop=NULL;
    Mesh_free(&mesh); 
}
static void my_sscanf(char * str,int* i1,int* i2,int *i3)
{
    int len=0;
    char** words=split(str,'/',&len);
    int len1=strlen(str),num=0;
    for(int i=0;i<len1;i++)
    {
        if(str[i]=='/')
        {
            num++;
        }
    } 
    int * indexs[3]={i1,i2,i3};
    if(num==2&&len==2)
    {
        indexs[1]=i3; 
    }  

    for(int i=0;i<len;i++)
    {
        sscanf(words[i],"%d",indexs[i]);
        free(words[i]);
    }
    SAFE_FREE(words);
}


void zydl_compress_texture(char* filename,char*outfilename)
{
#ifdef _WIN32
    FILE *infile;
    fopen_s(&infile,filename,"r");
#else
    FILE *infile=fopen(filename,"r");
#endif
    if(!infile)
    {
        printf("can't open this file %s\n",filename);
    }
    fseek(infile,0,SEEK_SET);
#ifdef _WIN32
    FILE*outfile;
    fopen_s(&outfile,outfilename,"w");
#else
    FILE *outfile=fopen(outfilename,"w");
#endif
    if(!outfile)
    {
        printf("can't open this file %s\n",outfilename);
    }
    fseek(outfile,0,SEEK_SET);
    char str[200]={NULL};
    Mesh mesh;
    Mesh_init(&mesh);
    mesh.dimension=0; 
    Node* node=NULL;
    while(!feof(infile))
    {
        fgets(str,200,infile);
        if(feof(infile))
        {
            break;
        } 
        if(str[0]=='v'&&str[1]=='t'&&str[2]==' ')
        {
            double point[3]={0,0,0};            
            sscanf(&str[3],"%lf %lf ",&point[0],&point[1]);
            template_v* v= mesh.create_vertexv(&mesh,point,3);
            node=node_overlying(node,v);
        }
    } 
    OC_Node* ocn=(OC_Node*)malloc(sizeof(OC_Node));
    oc_node_init_from_node(ocn,node);
    printf("%lf %lf %lf\n",ocn->loc_min[0],ocn->loc_min[1],ocn->loc_min[2]);
    printf("%lf %lf %lf\n",ocn->loc_max[0],ocn->loc_max[1],ocn->loc_max[2]);
    ocn->loc_max[2]=0.000001;ocn->loc_min[2]=-0.000001; 
    for(int i=0;i<10;i++)
    {
        oc_node_divide_all_leaves(ocn);
    } 
    //node=NULL;

    // printf("%lf %lf %lf\n",ocn->loc_min[0],ocn->loc_min[1],ocn->loc_min[2]);
    //printf("%lf %lf %lf\n",ocn->loc_max[0],ocn->loc_max[1],ocn->loc_max[2]);
    //printf("vexel size:%d vertices size:%d\n",oc_node_voxel_size(ocn),oc_node_vertices_size(ocn));
    int vt_id=0;
    for(auto ocit=octree_begin(ocn);ocit.it!=NULL;ocit++)
    { 
        if(ocit.it->value!=NULL)
        {
            for(Node*nit= ocit.it->value;nit!=NULL;nit=(Node*)(nit->Next))
            {
                template_v* v=(template_v*)(nit->value);
                v->prop=ocit.it;
            }
            int * value=(int*)malloc(sizeof(int));
            *value=vt_id;
            ocit.it->prop=value; 
            vt_id++;
        }
    }
    //printf("vt_id:%d\n",vt_id);
    fseek(infile,0,SEEK_SET);
    int mark=0;
    while(!feof(infile))
    {
        fgets(str,200,infile);
        if(feof(infile))
        {
            break;
        } 
        if(str[0]=='v'&&str[1]=='t'&&str[2]==' ')
        {
            if(mark==1)
            {
                continue;
            }
            vt_id=0;
            for(auto ocit=octree_begin(ocn);ocit.it!=NULL;ocit++)
            {
                if(ocit.it->value!=NULL)
                {
                    double point[2]={0,0};double num=0;
                    for(Node*nit= ocit.it->value;nit!=NULL;nit=(Node*)(nit->Next))
                    {
                        template_v* v=(template_v*)(nit->value);
                        point[0]+=v->point[0];point[1]+=v->point[1];
                        num=num+1;
                    }
                    vt_id++;
                    fprintf(outfile,"vt %lf %lf\n",point[0]/num,point[1]/num);
                }
            } 
          //  printf("once:%d\n",vt_id);
            mark=1;
        }
        else if(str[0]=='f'&&str[1]==' ')
        {
            fprintf(outfile,"f ");
            int temp_i=2;
            char str1[100]={NULL};
            while(sscanf(&str[temp_i],"%s",str1)!=EOF)
            {
                temp_i=temp_i+1+strlen(str1);
                int id=-1,id1=-1,id2=-1;
                my_sscanf(str1,&id,&id1,&id2);
                id--;id1--;id2--; 
                fprintf(outfile,"%d",id+1);
                template_v* v=mesh.get_vertexp(&mesh,id1);
                OC_Node* it=(OC_Node*)(v->prop);
                fprintf(outfile,"/%d",*((int*)(it->prop))+1);   
                if(id2>=0)
                {
                    fprintf(outfile,"/%d",id2+1); 
                }  
                fprintf(outfile," ");
                memset(str1,0,sizeof(char)*100);
            } 
            fprintf(outfile,"\n");
        }
        else
        {
            fprintf(outfile,"%s",str);
        }
    }

    for(Node*nit=node;nit!=NULL;nit=(Node*)(nit->Next))
    {

        template_v* v=(template_v*)(nit->value);
        
        OC_Node*it=(OC_Node*)(v->prop);
       
        SAFE_FREE(it->prop);
        v->prop=NULL;
    } 
    free_node(node);
    oc_node_free(ocn);//该函数已经释放node
    Mesh_free(&mesh);
    fclose(infile);
    fclose(outfile);
}

int main()
{
    zydl_simplified_mesh("texture_XYAB.obj"," test.obj",0.7);


    printf("end\n");

    return 0;
}